package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/smartwalle/alipay/v3"
	"github.com/smartwalle/xid"
)

var client *alipay.Client

const (
	kAppId = "9021000139668174"
	//私钥
	kPrivateKey = "MIIEowIBAAKCAQEApDwIYBFU4C1O5T/zCH0DP/lHiAW3H8yYIeja3AZLhxFOYGoCxOUi5G1R7oDAVodw7VFi1l+kMv8VW0zY8L4L9Rx8GPx41YhOneGlGGHqynm90QbedtApo6gaeDLCbPL11c9LjWGDc/BZQSeQ1YIhgUDpntT1uNp0U6KxKpNEXjHMdm0NKYwhBXrnz6W5Pjc9fGNGpTCQCPwc76mzX72JWO6nAuZ1hR6fONABOP6Bwx7gw61fOoSdrZc1C89Vbzg4XKeBZxNzkhIth2Eq5Uzzzd2sL7g+zyMh50d5q0TDmSCGKyVxbjtSYrK7InfR25iMFAdwRvmJI5qbBHXZkLa6MQIDAQABAoIBACzGPRZLw1L2pzqyy4Q16MvpHwpDXKBru9IK1JgFk1O4cSCx5h1dxrx13U9jhIxSkoPwmzyMiIxqpJzmf4JqnYHuiRqawiqc5an/WW4jS8p1lD1HXSERMTjv3H+GfyEKv+DRiex6eEcEcKDls4Ox3YaKduaZcoA59lrS0PAlhA8VnVELdRNRiAZ/DMF2QzGbu2Pgvvnicqkp715f3SCnn7HKEfbkT9LlYzQx7vBC4w/qnRWpLnOv4G3rxFziy/4LzTW0m48gO2AJOkVreQrr/JikA1MUiVdhC6nlMeFxnAIpyc3PQ9yiJZ6BRJbP1lhbggHxinVtzeBsLSY9HmwvIH0CgYEA8BRuspxXNvWNeRqWOkkO5/xvdnlX2V9N/yCzpELOuPK1GPjXGzs7eDjNEkTf0hIFNq8+xw778iBmth2vuH7Bymzt5WPjv7mIm8cmycSft0miwHfarmUI/pYfzbrP4bUoJ3cy+Ow+y11lqhFBwpJQ/RLgOb2QWKYkj8/n5yytxDMCgYEAryAPFlMN+t5hPigHZCMm27PKGy8iy9hlQB6qPe4GuP7hMnC7KjcDofg+YPWczCoetTV0Vp70zXRmJoQ452Pr29qxqvmL6TMnZzdBUmgzvbffy2vfDFbeRkrwSE6SHpoVSNRmN78JzlKAnieg5uvVdXN4GopETfWv9svP8maXhAsCgYEAhYxYJ7MgMMpXcE6B0tphGFssBdNqMtj5Ijqms3EWXXtlkgaaYeOJKU8vhEIb31d23vyNB14meIHdoODSpuacFmaRz1DJmBU1ELKvuP1Pez3S8w2479n0LuqDtoV8ril/tLRx/lYseCP0Ma53zixmMOEXKgdxDF8fZ4BXmBkacFECgYBFnBajJwxNhYtdq4eUlCxj9AkH2SqZoEBOxmGhEDX33sggxpJ+6zM0dHpHDBnxRWTDXqYJsFFaQURBVxlPUHZ2A23PqjWk0kJ9UodSfDzk0mvQbeZQ/06G3k64TxLhuxluw46F5he9vSmGs/XPFDra3rUEx6Ty4A+7p7PWhFUBlQKBgBCW+rBdbzZdOJQsYpoOas55FUNBUD9wmXnJ6yhuvivB0VY1RDVr123qkVz58msKoz6MQ3exlTDMpr7axTkNoqfaRf/KLbag0bi/Sv3k656Q5gI4im6ZntayYc4U8lo0+PO7w90qmA8lfD9TOZvZViYCCsHLe/ZNOsfXoeGfIBpC"

	// TODO 设置回调地址域名
	kServerDomain = ""
)

func main() {
	route()
	var err error
	//支付宝提供了用于开发时测试的 sandbox 环境，对接的时候需要注意相关的 app id 和密钥是 sandbox 环境还是 production 环境的。如果是 sandbox 环境，本参数应该传 false，否则为 true。
	if client, err = alipay.New(kAppId, kPrivateKey, false); err != nil {
		log.Println("初始化支付宝失败", err)
		return
	}
	// 加载证书
	// 加载应用公钥证书
	if err = client.LoadAppCertPublicKeyFromFile("appPublicCert.crt"); err != nil {
		log.Println("加载证书发生错误", err)
		return
	}
	// 加载支付宝根证书
	if err = client.LoadAliPayRootCertFromFile("alipayRootCert.crt"); err != nil {
		log.Println("加载证书发生错误", err)
		return
	}
	// 加载支付宝公钥证书
	if err = client.LoadAlipayCertPublicKeyFromFile("alipayPublicCert.crt"); err != nil {
		log.Println("加载证书发生错误", err)
		return
	}
	//接口内容加密
	//	if err = client.SetEncryptKey("iotxR/d99T9Awom/UaSqiQ=="); err != nil {
	log.Println("加载内容加密密钥发生错误", err)
	return
}
func route() {
	//路由函数
	r := gin.Default()
	r.GET("/alipay/pay", pay)
	r.GET("/alipay/callback", callback)
	r.GET("/alipay/notify", notify)
	r.Run(":8084")
}

func pay(c *gin.Context) {
	var tradeNo = fmt.Sprintf("%d", xid.Next())
	var p = alipay.TradePagePay{}
	p.NotifyURL = kServerDomain + "/alipay/notify"
	p.ReturnURL = kServerDomain + "/alipay/callback"
	p.Subject = "支付测试:" + tradeNo
	p.OutTradeNo = tradeNo
	p.TotalAmount = "20.00"
	p.ProductCode = "FAST_INSTANT_TRADE_PAY"

	url, _ := client.TradePagePay(p)
	c.Redirect(http.StatusTemporaryRedirect, url.String())
	//http.Redirect(writer, request, url.String(), http.StatusTemporaryRedirect)
}

func callback(c *gin.Context) {
	// 解析请求参数
	if err := c.Request.ParseForm(); err != nil {
		log.Println("解析请求参数发生错误", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "解析请求参数发生错误"})
		return
	}

	// 获取通知参数
	outTradeNo := c.Request.Form.Get("out_trade_no")

	// 验证签名
	if err := client.VerifySign(c.Request.Form); err != nil {
		log.Println("回调验证签名发生错误", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "回调验证签名发生错误"})
		return
	}

	log.Println("回调验证签名通过")

	// 查询订单状态
	var p = alipay.TradeQuery{}
	p.OutTradeNo = outTradeNo

	rsp, err := client.TradeQuery(c, p)
	if err != nil {
		log.Printf("验证订单 %s 信息发生错误: %s", outTradeNo, err.Error())
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("验证订单 %s 信息发生错误: %s", outTradeNo, err.Error())})
		return
	}

	if rsp.IsFailure() {
		log.Printf("验证订单 %s 信息发生错误: %s-%s", outTradeNo, rsp.Msg, rsp.SubMsg)
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("验证订单 %s 信息发生错误: %s-%s", outTradeNo, rsp.Msg, rsp.SubMsg)})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("订单 %s 支付成功", outTradeNo)})
}

func notify(c *gin.Context) {
	// 解析请求参数
	if err := c.Request.ParseForm(); err != nil {
		log.Println("解析请求参数发生错误", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "解析请求参数发生错误"})
		return
	}

	// 解析异步通知
	notification, err := client.DecodeNotification(c.Request.Form)
	if err != nil {
		log.Println("解析异步通知发生错误", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "解析异步通知发生错误"})
		return
	}

	log.Println("解析异步通知成功:", notification.NotifyId)

	// 查询订单状态
	var p = alipay.NewPayload("alipay.trade.query")
	p.AddBizField("out_trade_no", notification.OutTradeNo)

	var rsp *alipay.TradeQueryRsp
	if err := client.Request(c, p, &rsp); err != nil {
		log.Printf("异步通知验证订单 %s 信息发生错误: %s", notification.OutTradeNo, err.Error())
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("异步通知验证订单 %s 信息发生错误: %s", notification.OutTradeNo, err.Error())})
		return
	}

	if rsp.IsFailure() {
		log.Printf("异步通知验证订单 %s 信息发生错误: %s-%s", notification.OutTradeNo, rsp.Msg, rsp.SubMsg)
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("异步通知验证订单 %s 信息发生错误: %s-%s", notification.OutTradeNo, rsp.Msg, rsp.SubMsg)})
		return
	}

	log.Printf("订单 %s 支付成功", notification.OutTradeNo)

	client.ACKNotification(c.Writer)
}
